/*
 * 人人信用管理有限公司版权所有.
 * 
 * @Description:
 * 
 * @author: liyuliang
 * 
 * @date: 20 Apr 2017 10:44:16 am
 * 
 * 注意：本内容仅限于人人信用管理有限公司内部传阅，禁止外泄以及用于其他的商业目的.
 */

package com.rrx360.common.dao;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

public abstract class QueryEntry {

  /**
   * QL used to query.
   */
  StringBuffer ql;

  /**
   * QL used to query count.
   */
  StringBuffer countQl;

  /**
   * query parameters.
   */
  Map<String, Object> params = new HashMap<String, Object>();

  protected Long start;

  protected Integer fetchSize;

  Boolean qlContainsWhere = null;

  Boolean countContainsWhere = null;

  /** used to aoto generate param key. */
  private int paramSeq = 0;

  protected CommonDao commonDao;



  public CommonDao getCommonDao() {
    return commonDao;
  }

  public void setCommonDao(CommonDao commonDao) {
    this.commonDao = commonDao;
  }

  public QueryEntry() {

  }

  public QueryEntry(String ql, String countQl, Long start, Integer fetchSize) {
    if (ql != null) {
      this.ql = new StringBuffer(ql);
    }
    if (countQl != null) {
      this.countQl = new StringBuffer(countQl);
    }
    this.start = start;
    this.fetchSize = fetchSize;
  }

  public StringBuffer getQl() {
    return ql;
  }



  public StringBuffer getCountQl() {
    return countQl;
  }

  public void setCountQl(StringBuffer countQl) {
    this.countQl = countQl;
  }

  public void setQl(StringBuffer ql) {
    this.ql = ql;
  }



  public Map<String, Object> getParams() {
    return params;
  }



  public void setParams(Map<String, Object> params) {
    this.params = params;
  }


  public QueryEntry addCondition(String key, Object value) {
    addCondition(key, key, value);
    return this;
  }

  public QueryEntry addCondition(String key, Object value, boolean checkWhere) {
    addCondition(key, key, "=", value, checkWhere);
    return this;
  }
  
  
  public QueryEntry addCondition(String key, String keyParam, Object value) {
    addCondition(key, keyParam, "=", value);
    return this;
  }

  /**
   * 
   * @param key key name
   * @param keyParam value param in ql
   * @param cmp query operate eg.= > <
   * @param value param value
   * @return
   */
  public QueryEntry addCondition(String key, String keyParam, String cmp, Object value) {
    return joinCondition("and", key, keyParam, cmp, value);
  }

  public QueryEntry addCondition(String key, String keyParam, String cmp, Object value,
      boolean checkWhere) {
    return joinCondition("and", key, keyParam, cmp, value, false, checkWhere);
  }

  public QueryEntry addCon(String key, Object value) {
    addCon(key, key, value);
    return this;
  }

  public QueryEntry addCon(String key, Object value, boolean checkWhere) {
    addCon(key, key, "=", value, checkWhere);
    return this;
  }
  
  public QueryEntry addCon(String key, String keyParam, Object value) {
    addCon(key, keyParam, "=", value);
    return this;
  }
  
  public QueryEntry addCon(String key, String keyParam, String cmp, Object value) {
    return joinCondition("and", key, keyParam, cmp, value, true, true);
  }
  
  public QueryEntry addCon(String key, String keyParam, String cmp, Object value,
      boolean checkWhere) {
    return joinCondition("and", key, keyParam, cmp, value, true, checkWhere);
  }


  public QueryEntry orCondition(String key, Object value) {
    orCondition(key, key, value);
    return this;
  }
  
  public QueryEntry orCondition(String key, String keyParam, Object value) {
    orCondition(key, keyParam, "=", value);
    return this;
  }
  
  public QueryEntry orCondition(String key, String keyParam, String cmp, Object value) {
    return joinCondition("or", key, keyParam, cmp, value);
  }
  

  private void checkWhere() {
    // append where to ql
    if (qlContainsWhere == null) {
      if (ql != null && ql.indexOf(" where ") == -1) {
        ql.append(" where 1=1");
      }
      qlContainsWhere = true;
    }
    // append where to countQl
    if (countContainsWhere == null) {
      if (countQl != null && countQl.indexOf(" where ") == -1) {
        countQl.append(" where 1=1");
      }
      countContainsWhere = true;
    }
  }

  public QueryEntry addNull(String keyParam) {
    checkWhere();
    if (ql != null) {
      ql.append(" and " + keyParam + " is null");
    }
    if (countQl != null) {
      countQl.append(" and" + keyParam + " is null");
    }
    return this;
  }

  public QueryEntry addIfNull(String keyParam, Object value) {
    if (value != null && !(value instanceof String && "".equals(value))) {
      return this;
    }
    checkWhere();
    if (ql != null) {
      ql.append(" and " + keyParam + " is null");
    }
    if (countQl != null) {
      countQl.append(" and" + keyParam + " is null");
    }
    return this;
  }
 

  public QueryEntry joinCondition(String join, String key, String keyParam, String cmp,
      Object value) {
    return joinCondition(join, key, keyParam, cmp, value, false, true);
  }


  public QueryEntry joinCondition(String join, String key, String keyParam, String cmp,
      Object value, boolean containsEmpty, boolean checkWhere) {
    boolean isEmpty = value == null || (value instanceof String && "".equals(value))
        || (value instanceof Object[] && ((Object[]) value).length == 0)
        || (value instanceof List && ((List) value).size() == 0);

    if (!containsEmpty && isEmpty) {
      return this;
    }

    if (keyParam.indexOf(".") >= 0 || params.containsKey(keyParam)) {
      keyParam = "_param_" + paramSeq++;
    }
    if (checkWhere) {
      checkWhere();
    }
    StringBuffer temp = new StringBuffer();

    if (isEmpty) {
      temp.append(" " + join + " " + key + " is null");
    } else {

      if (value instanceof Object[] || value instanceof List) {
        Object[] vals;
        if (value instanceof Object[]) {
          vals = (Object[]) value;
        } else {
          vals = ((List<Object>) value).toArray();
        }
        if (vals.length == 1) {
          temp.append(" " + join + " " + key + "=:" + keyParam);
          params.put(keyParam, vals[0]);
        } else {
          temp.append(" " + join + " " + key + " in (");
          for (int i = 0; i < vals.length; i++) {
            temp.append(":" + keyParam + i + ",");
            params.put(keyParam + i, vals[i]);
          }
          temp.deleteCharAt(temp.length() - 1);
          temp.append(")");
        }
      } else {
        temp.append(" " + join + " " + key + " " + cmp + " :" + keyParam);
        params.put(keyParam, value);
      }
    }
    if (ql != null) {
      ql.append(temp);
    }
    if (countQl != null) {
      countQl.append(temp);
    }
    return this;
  }

  public QueryEntry addParam(String key, Object value) {
    params.put(key, value);
    return this;
  }

  public QueryEntry appendToQl(String info) {
    return append(info, true, false);
  }

  public QueryEntry appendToCountQl(String info) {
    return append(info, false, true);
  }

  public QueryEntry append(String info) {
    return append(info, true, true);
  }

  /**
   * Append a query part to QL.
   * 
   * @param info
   * @param toQl
   * @param toCountQl
   * @return
   */
  private QueryEntry append(String info, boolean toQl, boolean toCountQl) {
    if (StringUtils.isEmpty(info)) {
      return this;
    }

    if (!info.startsWith(" ")) {
      info = " " + info;
    }

    if (toQl && ql != null) {
      ql.append(info);
    }
    if (toCountQl && countQl != null) {
      countQl.append(info);
    }
    return this;
  }

  public abstract List exec(long start, int fetchSize);

  public abstract List exec();

  public abstract long execUpdate();

  public <T> T execUnique() {
    List result = exec();
    if (result.size() == 0) {
      return null;
    }
    return (T) result.get(0);
  }


  /**
   * Convert an QL to Count QL by 'from' identifier. If not suit, use set countQL directly plz.
   */
  protected void convertQlToCountQl() {
    if (ql == null) {
      throw new IllegalArgumentException("ql is null");
    }
    String sql = ql.toString();
    int start = sql.indexOf(" from ");
    if (start == -1) {
      start = sql.indexOf("from");
    }
    if (start == -1) {
      throw new IllegalArgumentException("can't find from ");
    }
    int end = sql.indexOf(" order by ");
    if (end == -1) {
      end = sql.length();
    }
    countQl = new StringBuffer("select count(*) " + ql.substring(start, end));
  }

  public abstract long execCount();

  public Long getStart() {
    return start;
  }

  public QueryEntry setStart(Long start) {
    this.start = start;
    return this;
  }

  public Integer getFetchSize() {
    return fetchSize;
  }

  public QueryEntry setFetchSize(Integer fetchSize) {
    this.fetchSize = fetchSize;
    return this;
  }


}
